/*
 *  Copyright (C) 2010 Jonathan MERCIER <bioinfornatics@fedoraproject.org>
 *
 *  JACoMode is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  JACoMode is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with JACoMode.  If not, see <http://www.gnu.org/licenses/>.
 */

package fr.ubdx1.jacomode.models;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;

/**
 *
 * @author Jonathan MERCIER <bioinfornatics@fedoraproject.org>,Louise-Amélie SCHMITT <louise-amelie.schmitt@etu.u-bordeaux1.fr>,Florence Maurier <florence_maurier@orange.fr>,Marianne Dussart <marianne.dussart@orange.fr>,Nicolas Fontaine <nifontaine@gmail.com>
 */
public final class AcomLaunch
{
    /**
     * List of clients
     */
    protected static ArrayList<String[]> client = new ArrayList<String[]>();
    /**
     * Boolean indicating if ACoM is running
     */
    protected static boolean isRunning;
    /**
     * The next serial number to be assigned
     */
    protected static int nextSerialNum = 0;

    private static volatile AcomLaunch instance;

	private static ThreadLocal<?> serialNum = new ThreadLocal<Object>()
    {
		@Override
		protected synchronized Object initialValue()
        {
			return new Integer(nextSerialNum++);
		}
	};

    /**
     * return the session's identifier
     * @return serialNum
     */
	public static synchronized int getIdentifier()
    {
		return ((Integer) (serialNum.get())).intValue();
	}

	private static HashMap<Integer, HashMap<String, AcomLaunch> > execPool =  new HashMap<Integer, HashMap<String, AcomLaunch>>();

    /**
     * return the ACoM execution
     * @param id
     * @param mail
     * @param fileOut
     * @param fileDat
     * @param rowNumber
     * @param epsilon
     * @param sigma
     * @return instance
     */
    private static AcomLaunch getAcomLaunch(String id, String mail, File fileOut,File fileDat, String rowNumber, String epsilon,String sigma)
    {
        int identifier                              = AcomLaunch.getIdentifier();
        HashMap<String, AcomLaunch> execCollection  = AcomLaunch.execPool.get(new Integer(identifier));

        if (execCollection != null)
        {
            instance = execCollection.get(id);
            if (instance == null)
            {
                synchronized(AcomLaunch.class)
                {
                    instance = new AcomLaunch(mail, fileOut, fileDat, rowNumber, epsilon,sigma);
                }
                execCollection.put(id, instance);
            }
        }
        else
        {
            execCollection = new HashMap<String, AcomLaunch>();
            synchronized(AcomLaunch.class)
            {
                instance = new AcomLaunch(mail, fileOut, fileDat, rowNumber, epsilon,sigma);
            }
            execCollection.put(id, instance);
            AcomLaunch.putInToExecPool(execCollection);
        }
        return instance;
    }

    /**
     * return the ACoM execution
     * @param id
     * @param mail
     * @param fileOut
     * @param fileDat
     * @param rowNumber
     * @param epsilon
     * @param sigma
     * @return serialNum
     */
    public static AcomLaunch getAcom(String id, String mail, File fileOut,File fileDat, String rowNumber, String epsilon,String sigma)
    {

        return AcomLaunch.getAcomLaunch(id, mail, fileOut, fileDat, rowNumber, epsilon, sigma);
    }

    /**
     * Add a process
     * @param execCollection
     * @return serialNum
     */
    private static void putInToExecPool( HashMap<String, AcomLaunch> execCollection )
    {
        AcomLaunch.execPool.put( new Integer(AcomLaunch.getIdentifier()), execCollection);
    }

    /**
     * Add a request to launch ACoM / constructor
     * @param mail
     * @param fileOut
     * @param fileDat
     * @param rowNumber
     * @param epsilon
     * @param sigma
     */
    private AcomLaunch(String mail, File fileOut,File fileDat, String rowNumber, String epsilon,String sigma)
    {
        String tmp[] = {fileOut.getAbsolutePath(), fileDat.getAbsolutePath(), rowNumber, epsilon, sigma, mail};

        client.add( tmp );
    }

    /**
     * Add a request to launch ACoM / constructor
     * @param userDirectories
     * @throws IOException
     * @throws InterruptedException
     **/
    public void exec(String userDirectories) throws IOException, InterruptedException
    {
        if (client.size() > 0)
        {
            while (isRunning)
            {
                //do Nothing
            }
            run(userDirectories);
        }
    }
    /**
     * Run ACoM
     * @param userDirectories
     * @throws IOException
     * @throws InterruptedException
     **/
    public void run(String userDirectories) throws IOException, InterruptedException
    {
        isRunning                   = true;
        String[]    cmd             = client.remove(0);
        String      fileOut         = cmd[0];
        String      fileDat         = cmd[1];
        String      rowNumber       = cmd[2];
        String      epsilon         = cmd[3];
        String      sigma           = cmd[4];

        final ProcessBuilder processus  = new ProcessBuilder("/usr/bin/acom", fileDat, fileOut, rowNumber,epsilon, sigma);
        processus.directory( new File(userDirectories) );
        processus.redirectErrorStream(true);
        Process p = processus.start();
//                InputStream err = p.getErrorStream();
        InputStream out = p.getInputStream();
        BufferedReader input = new BufferedReader( new InputStreamReader(out) );
        String line = "";
        while ( ( line = input.readLine() ) != null)
        {
            System.out.println(line);
        }
        input.close();
        p.waitFor();
        isRunning = false;
    }

    /**
     * Destroy the identifier
     **/
    @Override
    public void finalize()
    {
        int identifier                              = AcomLaunch.getIdentifier();
        HashMap<String, AcomLaunch> execCollection  = AcomLaunch.execPool.get(identifier);
        execCollection.clear();
    }
}